/*
 * CAudioFrameReader.cpp
 *
 *  Created on: 2019年1月15日
 *      Author: terry
 */

#include "CAudioFrameReader.h"
#include "AVFramePtr.h"
#include <assert.h>
#include "CLog.h"


namespace av
{

CAudioFrameReader::CAudioFrameReader():
    m_url(),
    m_params(),
    m_channels(),
    m_sampleRate(),
    m_fmt(AV_SAMPLE_FMT_NONE),
    m_fmtContext(),
    m_swrContext(),
    m_audioIndex(-1)
{
}

CAudioFrameReader::~CAudioFrameReader()
{
    close();
}

bool CAudioFrameReader::open(const std::string& url, const std::string& params, int channels, int sampleRate, AVSampleFormat fmt)
{
	CLog::info("CAudioFrameReader::open. url: %s, chl:%d, freq: %d, fmt: %d\n", url.c_str(), channels, sampleRate, fmt);

    m_url = url;
    m_params = params;

    m_channels = channels;
    m_sampleRate = sampleRate;
	m_fmt = fmt;

    if (!openFile(url, params))
    {
        return false;
    }

    return true;
}

void CAudioFrameReader::close()
{
    if (m_fmtContext)
    {
        if (m_audioIndex >= 0)
        {
            AVCodecContext* codecCtx = m_fmtContext->streams[m_audioIndex]->codec;
            avcodec_close(codecCtx);
        }

        avformat_close_input(&m_fmtContext);
        m_fmtContext = NULL;
    }

    if (m_swrContext)
    {
        swr_close(m_swrContext);
        swr_free(&m_swrContext);
        m_swrContext = NULL;
    }

}

bool CAudioFrameReader::isOpen()
{
    return m_fmtContext != NULL;
}

int CAudioFrameReader::read(AVFrame* frame)
{
    int rc = 0;
    while (true)
    {
        rc = readFrame(frame);
        if (rc == 0)
        {
            return rc;
        }

        if (rc != AVERROR(EAGAIN))
        {
            break;
        }
    }
    return rc;
}

bool CAudioFrameReader::getFormat(int& channels, int& sampleRate, AVSampleFormat& fmt)
{
    channels = m_channels;
    sampleRate = m_sampleRate;
    fmt = m_fmt;
    return true;
}

bool CAudioFrameReader::openFile(const std::string& url, const std::string& params)
{
    int rc = avformat_open_input(&m_fmtContext, url.c_str(), NULL, NULL);
    if (rc != 0)
    {
        return false;
    }

    rc = avformat_find_stream_info(m_fmtContext, NULL);

    AVCodecContext* codecCtx = 0;
    AVCodec* audioCodec = NULL;
    {
        int idx = av_find_best_stream(m_fmtContext, AVMEDIA_TYPE_AUDIO, -1, -1, &audioCodec, 0);
        if (audioCodec)
        {
            AVStream* stream = m_fmtContext->streams[idx];
            codecCtx = stream->codec;

            AVSampleFormat sampleFmt = codecCtx->sample_fmt;
            int sampleRate = codecCtx->sample_rate;
            int channels = codecCtx->channels;

            m_audioIndex = idx;
        }
    }

    if (m_audioIndex < 0)
    {
        close();
        return false;
    }

    rc = avcodec_open2(codecCtx, audioCodec, NULL);

    return true;
}

int CAudioFrameReader::resample(AVFrame* inFrame, AVFrame* outFrame)
{
	if (m_swrContext == NULL)
	{
		m_swrContext = swr_alloc();

		swr_config_frame(m_swrContext, outFrame, inFrame);

		int rc = swr_init(m_swrContext);
		if (rc != 0)
		{
			CLog::error("swr_init failed. rc:%d\n", rc);
		}
	}
	else
	{
		int delay = swr_get_delay(m_swrContext, outFrame->sample_rate);

		outFrame->nb_samples = inFrame->nb_samples + delay;
		av_frame_get_buffer(outFrame, 1);
	}

	swr_convert_frame(m_swrContext, outFrame, inFrame);

	return outFrame->nb_samples;
}

int CAudioFrameReader::readFrame(AVFrame* frame)
{
    if (!isOpen())
    {
        return AVERROR(EINVAL);
    }

    AVPacket pkt;
    av_init_packet(&pkt);
    int rc = av_read_frame(m_fmtContext, &pkt);
    if (rc != 0)
    {
        return rc;
    }

    if (pkt.stream_index != m_audioIndex)
    {
        av_packet_unref(&pkt);
        return AVERROR(EAGAIN);
    }

    AVStream* stream = m_fmtContext->streams[pkt.stream_index];

    /// 转换时间戳到 1/1000000
    AVRational destRate = av_make_q(1, m_sampleRate);
    //av_packet_rescale_ts(&pkt, stream->time_base, destRate);

    //std::cout << "input pkt. " << pkt.stream_index << " pts: " << pkt.pts << std::endl;

    rc = AVERROR(EAGAIN);

    av::AVFramePtr decFrame(av_frame_alloc(), av::avframe_free);

    int gotFrame = 0;
    int ret = avcodec_decode_audio4(stream->codec, decFrame.get(), &gotFrame, &pkt);
    if (gotFrame)
    {
		decFrame->pts = pkt.dts;
		if (decFrame->channel_layout <= 0)
		{
			decFrame->channel_layout = av_get_default_channel_layout(decFrame->channels);
		}

        frame->format = m_fmt;
        frame->channels = m_channels;
        frame->channel_layout = av_get_default_channel_layout(m_channels);
        frame->sample_rate = m_sampleRate;
        
		//int64_t pts = av_rescale(decFrame->pts, frame->sample_rate, decFrame->sample_rate);

		AVRational destRate = av_make_q(1, m_sampleRate);
		int64_t pts = av_rescale_q(pkt.dts, stream->time_base, destRate);
		frame->pts = pts;

        int ret = resample(decFrame.get(), frame);
        if (ret > 0)
        {
            rc = 0;
        }
    }

    av_packet_unref(&pkt);

    return rc;
}



} /* namespace av */
